package report3.classes;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Scanner;

/**
 * 分段存储
 * Author：zouran
 * Date：2023/5/4  15:17
 */
public class Paragraph {
    //地址范围,[0,100]
    private final Integer maxAddress = 101;
    //标记地址是否占用,未占用为true
    private final boolean[] addressAvailable = new boolean[maxAddress];
    //页表项对应关系，对应基址
    private final Map<Integer, Integer> map = new HashMap<>();
    //基址对应段大小的map
    private final Map<Integer, Integer> paragraphMap = new HashMap<>();
    //随机页面数组
    private final int[] randParagraphSizeList = {1, 2, 4, 8};
    //页表项个数
    private Integer count;

    public Paragraph() {
        for (int i = addressAvailable.length - 1; i >= 0; i--) {
            addressAvailable[i] = true;
        }
    }

    public void setCount(Integer count) {
        this.count = count;
    }

    //判断地址是否冲突
    public boolean isNotOverlap(int begin, int size) {
        for (int i = begin; i < begin + size; i++) {
            if (!addressAvailable[i]) {
                return false;
            }
        }
        return true;
    }

    public int calculateMaxAddress() {
        int sum = 0;
        for (Integer key : map.keySet()) {
            sum += paragraphMap.get(map.get(key));
        }
        return sum;
    }

    /**
     * 寻找冲突段，先找到冲突地址前的基础比较值，再以此寻找冲突地址前的最大地址
     */
    public int findParagraph(int begin, int size) {
        Integer max = 0;
        for (int i = begin; i < begin + size; i++) {
            if (!addressAvailable[i]) {
                for (Integer key : map.keySet()) {
                    if (map.get(key) <= i) {
                        max = key;
                        break;
                    }
                }
                for (Integer key : map.keySet()) {
                    if (map.get(key) <= i && map.get(key) >= map.get(max)) {
                        max = key;
                    }
                }
                return max;
            }
        }
        return max;
    }

    //更新存储信息
    public void update(int i, int begin, int size) {
        map.put(i, begin);
        paragraphMap.put(begin, size);
        for (int j = begin; j < size + begin; j++) {
            addressAvailable[j] = false;
        }
    }

    //读取用户输入
    public void readScannerMap() {
        int i = 0;
        while (i < count) {
            System.out.printf("请输入页表中第%d段对应的基址和段长:", i);
            Scanner scanner = new Scanner(System.in);
            int begin = scanner.nextInt();
            int size = scanner.nextInt();
            if (isNotOverlap(begin, size)) {
                update(i, begin, size);
                i++;
            } else {
                int paragraph = findParagraph(begin, size);
                System.out.printf("您输入的数据有误，与第%d号分段（基址%d，段长%d）冲突，请重新输入第%d号分段对应的基址和段长！\n",
                        paragraph, map.get(paragraph), paragraphMap.get(map.get(paragraph)), i);
            }
        }
    }

    //随机生成段的大小与基址
    public void setRandParagraph() {
        int i = 0;
        while (i < count) {
            Random random = new Random();
            int begin = random.nextInt(maxAddress);
            int size = randParagraphSizeList[random.nextInt(randParagraphSizeList.length)];
            //若越界
            if (begin + size >= maxAddress) continue;
            //若地址未重叠
            if (isNotOverlap(begin, size)) {
                System.out.printf("请输入页表中第%d段对应的基址和段长: %d %d\n", i, begin, size);
                update(i, begin, size);
                i++;
            } else {
                int paragraph = findParagraph(begin, size);
                System.out.printf("请输入页表中第%d段对应的基址和段长: %d %d\n", i, begin, size);
                System.out.printf("您输入的数据有误，与第%d号分段（基址%d，段长%d）冲突，请重新输入第%d号分段对应的基址和段长！\n",
                        paragraph, map.get(paragraph), paragraphMap.get(map.get(paragraph)), i);
            }
        }
    }

    /**
     * 计算位置信息
     * 返回集合
     * {
     * paragraph:段
     * offset:偏移位置
     * position:物理位置
     * status:位置是否合法
     * }
     */
    public Map<String, Integer> calculate(int position) {
        Map<String, Integer> map1 = new HashMap<>();
        //若越界(位置为负数或者超出最后一段)
        if (position < 0 || calculateMaxAddress() <= position) {
            map1.put("status", 0);
            return map1;
        }
        //0开始
        int paragraph = 0;
        //位于段内且逻辑地址大于当前段大小
        while (paragraph < count && position > paragraphMap.get(map.get(paragraph))) {
            position = position - paragraphMap.get(map.get(paragraph));
            paragraph++;
        }
        //添加返回值
        map1.put("paragraph", paragraph);
        map1.put("begin", map.get(paragraph));
        map1.put("offset", position);
        map1.put("position", map.get(paragraph) + position);
        map1.put("status", 1);
        return map1;
    }

    public void printMap() {
        System.out.println("段号\t基址\t段长");
        for (Integer key : map.keySet()) {
            System.out.printf("%d\t%d\t%d\n", key, map.get(key), paragraphMap.get(map.get(key)));
        }
    }
}
